package jxlog

import (
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"
)

const (
	dateFormat        = "20060102"
	logDir            = "./logs" // 默认日志目录
	cleanupInterval   = 24 * time.Hour
	defaultMaxAgeDays = 10
)

type logFile struct {
	file *os.File
	date string
	mu   sync.Mutex // 文件级锁
}

type Logger struct {
	logDir     string
	maxAgeDays int
	files      sync.Map // key: logType, value: *logFile
	stopOnce   sync.Once
	stopChan   chan struct{}
}

func NewLogger(path ...string) *Logger {
	_logdir := logDir
	if len(path) > 0 {
		_logdir = path[0]
	}
	l := &Logger{
		logDir:     _logdir,
		maxAgeDays: defaultMaxAgeDays,
		stopChan:   make(chan struct{}),
	}
	go l.startCleanup()
	return l
}

func (l *Logger) SetLogDir(dir string) {
	l.logDir = dir
}

func (l *Logger) SetMaxAgeDays(days int) {
	l.maxAgeDays = days
}

// 通用日志方法
func (l *Logger) Log(kind, format string, args ...interface{}) {
	// 1. 获取或创建日志文件（无锁化加载）
	val, _ := l.files.LoadOrStore(kind, &logFile{})
	lf := val.(*logFile)

	// 2. 检查日期变更（最小化锁范围）
	currentDate := time.Now().Format(dateFormat)
	if lf.date != currentDate {
		lf.mu.Lock()
		// 双重检查（避免竞争）
		if lf.date != currentDate {
			l.rotateFile(lf, currentDate, kind)
		}
		lf.mu.Unlock()
	}

	// 3. 写入日志（仅在写入时加锁）
	lf.mu.Lock()
	timestamp := time.Now().Format("2006-01-02 15:04:05")
	var argsStr string
	if len(args) > 0 {
		argsStr = " " + fmt.Sprint(args...)
	}

	// 拼接完整的日志消息
	msg := fmt.Sprintf("[%s] [%s] %s %s\n", timestamp, strings.ToUpper(kind), format, argsStr)
	_, err := io.WriteString(lf.file, msg)
	lf.mu.Unlock()

	if err != nil {
		fmt.Printf("写入日志失败: %v\n", err)
	}
}

// 预定义日志级别快捷方法
func (l *Logger) Debug(format string, args ...interface{}) {
	l.Log("debug", format, args...)
}

func (l *Logger) Info(format string, args ...interface{}) {
	l.Log("info", format, args...)
}

func (l *Logger) Error(format string, args ...interface{}) {
	l.Log("error", format, args...)
}

func (l *Logger) rotateFile(lf *logFile, newDate, kind string) {
	defer func() { lf.date = newDate }()

	// 关闭旧文件
	if lf.file != nil {
		_ = lf.file.Close()
	}

	// 创建新文件
	filename := fmt.Sprintf("%s_%s.log", newDate, kind)
	filePath := filepath.Join(l.logDir, filename)
	if err := os.MkdirAll(l.logDir, 0755); err != nil {
		panic(err)
	}

	file, err := os.OpenFile(filePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		panic(err)
	}
	lf.file = file
}

func (l *Logger) startCleanup() {
	ticker := time.NewTicker(cleanupInterval)
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			l.cleanupOldLogs()
		case <-l.stopChan:
			return
		}
	}
}

func (l *Logger) cleanupOldLogs() {
	cutoff := time.Now().Add(-time.Duration(l.maxAgeDays) * 24 * time.Hour)
	err := filepath.Walk(l.logDir, func(path string, info os.FileInfo, err error) error {
		if err != nil || info.IsDir() {
			return nil
		}

		// 解析文件名中的日期部分
		base := filepath.Base(path)
		dateStr := strings.Split(base, "_")[0]
		if len(dateStr) != 8 {
			return nil
		}

		fileDate, err := time.Parse(dateFormat, dateStr)
		if err != nil {
			return nil
		}

		if fileDate.Before(cutoff) {
			return os.Remove(path)
		}
		return nil
	})

	if err != nil {
		fmt.Printf("清理日志失败: %v\n", err)
	}
}

func (l *Logger) Close() {
	l.stopOnce.Do(func() {
		close(l.stopChan)
		l.cleanupOldLogs() // 最后清理一次
		l.files.Range(func(key, value interface{}) bool {
			lf := value.(*logFile)
			lf.mu.Lock()
			_ = lf.file.Close()
			lf.mu.Unlock()
			return true
		})
	})
}
